#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <signal.h>
#include <syslog.h>
#include <netinet/in.h>
#include <linux/netlink.h>
#include <linux/connector.h>
#define _LINUX_TIME_H
#include <linux/cn_proc.h>
#include "sched_relate.h"
#include "public_relate.h"


char *procname = "lbdaemon";

/* used for EXEC/PIPE/SHM ... */
struct list_head task_relate;
/* used for user lbconfig */
struct list_head user_relate;

static void init_lists(void)
{
    INIT_LIST_HEAD(&task_relate);
    INIT_LIST_HEAD(&user_relate);
}

static struct lb_config *lbc_init(const char *name, int type, struct list_head *head)
{	
	struct lb_config *lbc = malloc(sizeof(*lbc));
	strncpy(lbc->name, name, RELEN);
	lbc->uid = 0;
	lbc->type = type;
	INIT_LIST_HEAD(&lbc->head);

	list_add_rcu(&lbc->list, head);
	lbc->refcount = 0;
	RDprint("new lbc name: %s\n", lbc->name);
	return lbc;
}

/* called when task exit */
 void exit_relate(int pid)
{
	struct task_struct *p = find_task_by_vpid(pid);
    /* when pid is forked before program running, it's NULL */
    if (p == NULL)
        return;
    RDprint("   EXIT NAME: %s pid:%d\n", p->name, p->pid);
	struct task_relate *tr = p->tr;
	/* no task relationship */
	if (!tr)
		goto ret;

	if (tr->exec_lbc != NULL)
	{
		list_del_init(&tr->exec_relate);
		RDprint("   Exit delete:%s, type:exec\n", p->name );
		if ( --tr->exec_lbc->refcount == 0)
		{
				list_del_init(&tr->exec_lbc->list);
                free(tr->exec_lbc);
                tr->exec_lbc = NULL;
		}
	}
	if (tr->user_lbc != NULL)
	{
		list_del_init(&tr->user_relate);
		RDprint("   Exit delete:%s, type:user\n", p->name );
		if ( --tr->user_lbc->refcount == 0)
		{
    /* user_lbc shouldn't delete after task exit ?? */
				//list_del_init(&tr->user_lbc->list);
                //free(tr->user_lbc);
                tr->user_lbc = NULL;
		}
	}

	if (tr->shm_lbc != NULL)
	{
		list_del_init(&tr->shm_relate);
		RDprint("   Exit delete:%s, type:shm\n", p->name );
		if ( --tr->shm_lbc->refcount == 0)
		{
				list_del_init(&tr->shm_lbc->list);
                free(tr->shm_lbc);
                tr->shm_lbc = NULL;
		}
	}
	
	if ( tr->pipe_lbc != NULL)
	{
		list_del_init(&tr->pipe_relate);
		RDprint("   Exit delete:%s, type:pipe\n", p->name );
		if ( --tr->pipe_lbc->refcount == 0)
		{
				list_del_init(&tr->pipe_lbc->list);
                free(tr->pipe_lbc);
                tr->pipe_lbc = NULL;
		}
	}
	
    free(tr);
	p->tr = NULL;	/* ?? */
ret:
    task_free(p);
	return;
}

struct task_relate *init_task_relate(struct task_struct *p)
{
		/* will be kfreed in exit_relate() */
		struct task_relate *tr = malloc(sizeof(*tr));
        memset(tr, 0, sizeof(*tr));
		tr->p = p;
		p->tr = tr;

		INIT_LIST_HEAD(&tr->exec_relate);
		INIT_LIST_HEAD(&tr->shm_relate);
		INIT_LIST_HEAD(&tr->pipe_relate);
		INIT_LIST_HEAD(&tr->user_relate);

		tr->exec_lbc = NULL;
		tr->shm_lbc  = NULL;
		tr->user_lbc = NULL;
		tr->pipe_lbc = NULL;
		return tr;
}


/* called from do_fork() 
   record_shm_fork() will do SHM relate
   @p: parent 
   @c: child
	user_relate: nochange
	exec_relate: nochange */
void record_fork(int ppid, int cpid)
{
    struct task_struct *p;
    struct task_struct *c;
    p = find_task_by_vpid(ppid);
    if (p == NULL)
       p =  create_task(ppid);

    c = create_task(cpid);

	struct task_relate *ptr = p->tr;
	struct task_relate *ctr = c->tr;

	/* if p->tr == NULL, just return */
	if (!ptr)
	{
		c->tr = NULL;
		return;
	}

	ctr = init_task_relate(c);
	/* make the same as parent's task_relate */
	/* record USER_RELATE 
	   no need to send message to kernel cauz do_fork()
	   *p = *t copy this (so when do_exec() need to clear it) */
    if (ptr->user_lbc != NULL)
    {
        list_add_rcu(&ctr->user_relate, &ptr->user_lbc->head);
        ctr->user_lbc = ptr->user_lbc;
		ctr->user_lbc->refcount++;
    }
	/* record EXEC_RELATE */
	if (ptr->exec_lbc != NULL)
	{
		list_add_rcu(&ctr->exec_relate, &ptr->exec_lbc->head);
		ctr->exec_lbc = ptr->exec_lbc;
		ctr->exec_lbc->refcount++;
	}
	//TODO: PIPE_RELATE
	return ;
}

static int latestcpu(pid_t pid)
{
    char buf[PATH_MAX];
    char *bp;
    int fd = -1;
    int cpu = -1;

    if (pid == 0)
	snprintf(buf, sizeof(buf), "/proc/self/stat");
    else
	snprintf(buf, sizeof(buf), "/proc/%d/stat", pid);

    if ((fd = open(buf, O_RDONLY)) < 0) {
	errno = ESRCH;		// no such file -> no such PID(process)
	goto err;
    }
    if (read(fd, buf, sizeof(buf)) < 1)
	goto err;
    close(fd);

    bp = strrchr(buf, ')');
    if (bp)
	sscanf(bp + 1, "%*s %*u %*u %*u %*u %*u %*u %*u " "%*u %*u %*u %*u %*u %*u %*u %*u %*u %*u " "%*u %*u %*u %*u %*u %*u %*u %*u %*u %*u " "%*u %*u %*u %*u %*u %*u %*u %*u %u",	/* 37th field past ')' */
	       &cpu);
    if (cpu < 0)
	errno = EINVAL;
    return cpu;
  err:
    if (fd >= 0)
	close(fd);
    return -1;
}

static void sendmsg_to_kernel(struct user_config *uc, int pid)
{
	int sd;
	struct sockaddr_nl daddr;
	int group;
	struct nlmsghdr *nlhdr = NULL;
	struct msghdr msg;
	struct iovec iov;
	struct cn_msg *cnmsg;
	size_t size;
	int ret;
	struct kernel_relate_msg krm;
	size = sizeof(krm);
	memset(&krm, 0, size);
	krm.magic = RELATE_MAGIC;
#ifdef RELATE_DEBUG
    krm.d = 1;
#endif
	krm.pid = pid;
	if (uc != NULL)
	{
		krm.numaid = uc->numaid;
		if (uc->numaid != -1)
		{
			krm.numaid = uc->numaid;
			krm.what = SETHARD;
		}else if(uc->numaid == -1 && uc->latestcpu == -1){
			uc->latestcpu = latestcpu(pid);
			krm.latestcpu = uc->latestcpu;
			krm.what = SETGUESS;
		}
		else{
		krm.latestcpu = uc->latestcpu;
		krm.what = SETGUESS;
		}
	}else
		krm.what = CLEAR;
	group = CN_IDX_RELATE_TOKERNEL;
	sd = socket(PF_NETLINK, SOCK_RAW, NETLINK_CONNECTOR);
	if (sd < 0)
	{
	    syslog(LOG_USER|LOG_INFO, "%s: socket open error\n", procname);
        goto out;
	}
	nlhdr = (struct nlmsghdr *) malloc (NLMSG_SPACE (MAX_MSGSIZE));
	if (nlhdr == NULL)
		goto out;
	daddr.nl_family = AF_NETLINK;
	daddr.nl_pid = 0;
	daddr.nl_groups = CN_IDX_RELATE_TOKERNEL;
	if (bind(sd, (struct sockaddr *) &daddr, sizeof (daddr)) < 0)
	{
	    syslog(LOG_USER|LOG_INFO, "%s: bind error\n", procname);
		goto ret_free;
	}
	ret = setsockopt(sd, SOL_NETLINK,1, &group, sizeof(group));
	if (ret < 0)
	{
	  	syslog(LOG_USER|LOG_INFO, "%s: setsocket error\n", procname);
		goto ret_free;
	}
	memset (nlhdr, 0, sizeof (NLMSG_SPACE (MAX_MSGSIZE)));
	memset (&iov, 0, sizeof (struct iovec));
	memset (&msg, 0, sizeof (struct msghdr));
	cnmsg = (struct cn_msg *) NLMSG_DATA (nlhdr);
	memcpy(cnmsg->data, &krm, size);
	nlhdr->nlmsg_len = NLMSG_LENGTH (sizeof (struct cn_msg) + size);
	nlhdr->nlmsg_pid = 0;
	nlhdr->nlmsg_flags = 0;
	cnmsg->id.idx = CN_IDX_RELATE_TOKERNEL;
	cnmsg->id.val = CN_VAL_RELATE_TOKERNEL;
	cnmsg->seq = 0;
	cnmsg->ack = 0;
	cnmsg->len = size;	/* user_message length */
	iov.iov_base = (void *) nlhdr;
	iov.iov_len = nlhdr->nlmsg_len;
	msg.msg_name = (void *) &daddr;
	msg.msg_namelen = sizeof (daddr);
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	ret = sendmsg(sd, &msg, 0);
	if (ret < 0)
	  syslog(LOG_USER|LOG_INFO,"%s: sendmsg error\n", procname);
ret_free:
	free(nlhdr);
out:
	close(sd);
	return ;
	
}

/* check kuc->name filename is equal or
   kuc->path[255]->.... filename is equal */
static int is_userconfig(const struct user_config *kuc, const char *filename)
{
    char *p;
    int i;
    if (!strncmp(kuc->name, filename, RELEN))
        return 1;
    for(i=0, p=kuc->path[i]; p!=NULL; p=kuc->path[++i])
        if (!strncmp(p, filename, RELEN))
            return 1;
   return 0; 
}

/* called from do_exec()
    filename is FULL PATH 
   if a task->tr == NULL, malloc and init it */
void record_exec(int pid, const char *filename)
{
	struct lb_config *lbc;
	struct user_config *kuc;
	struct task_relate *tr;
    struct task_struct *p = find_task_by_vpid(pid);
    if (p == NULL)
        return;
	tr = p->tr;
    /* replace the original name: A:fork() -> exec(B) -> B */
    strncpy(p->name, filename, RELEN);
	/* if exec a program, relationship comes 
	   if tr is not null, clear the exec_relate */
	if (tr != NULL)
	{	
		/* del original exec relate */
		if (tr->exec_lbc != NULL)
		{
			RDprint("record delete at exec:%s, type:exe\n", p->name );
			list_del_init(&tr->exec_relate);
			if ( --tr->exec_lbc->refcount == 0)
			{
				list_del_init(&tr->exec_lbc->list);
                free(tr->exec_lbc);
			}
			tr->exec_lbc = NULL;
		}
		/* del original user relate */
		if (tr->user_lbc != NULL)
		{
			RDprint("record delete at exec:%s, type:exe\n", p->name );
			list_del_init(&tr->user_relate);
			if ( --tr->user_lbc->refcount == 0)
			{
				/* same at line:77*/
				//list_del_init(&tr->user_lbc->list);
                //free(tr->user_lbc);
			}
			tr->user_lbc = NULL;
			sendmsg_to_kernel(NULL, p->pid);
		}
		/* share memory should be deattached when EXEC */
		if (tr->shm_lbc != NULL)
		{
			RDprint("record delete at exec:%s, type:shm\n", p->name );
			list_del_init(&tr->shm_relate);
			if ( --tr->shm_lbc->refcount == 0)
			{
					list_del_init(&tr->shm_lbc->list);
                    free(tr->shm_lbc);
			}
			tr->shm_lbc = NULL;
		}
	}
	else
		tr = init_task_relate(p);

	/* record user_relate */
	list_for_each_entry(kuc, &user_relate, list) {
//		if(kuc->uid != p->uid)
//			continue;
		if(is_userconfig(kuc, filename))
		{
			list_add_rcu(&tr->user_relate, &kuc->head);
			tr->user_lbc= kuc;
			kuc->refcount++;
			sendmsg_to_kernel(kuc, tr->p->pid);
			RDprint("record_user: %s refcount:%d\n", filename, kuc->refcount);
		}
	}
	
	/* record exec_relate */
	RDprint("record_exec: %s\n", filename);
	list_for_each_entry(lbc, &task_relate, list) {
		if (lbc->type != EXEC_RELATE)
				continue;
//		if (p->uid != lbc->uid)	/* not the same user */
//				continue; TODO
		/* get a lb_config, compare it */
		if (!strncmp(filename, lbc->name, RELEN))
		{
			/* the same, attach currnet to lbc->head*/
			list_add_rcu(&tr->exec_relate, &lbc->head);
			tr->exec_lbc = lbc;
			lbc->refcount++;
			RDprint("attach relate to %s, count:%d\n", lbc->name, lbc->refcount);
			return;
		}
	}

	/* not match, create a entry */
	lbc =  lbc_init(filename, EXEC_RELATE, &task_relate);
	list_add_rcu(&tr->exec_relate, &lbc->head);
	lbc->refcount = 1;
	tr->exec_lbc = lbc;
	return;
}

static int show_list_pid(int pid, int req_pid, const char *path)
{
    struct task_struct *p;
    FILE *fp;
    if ( (fp = fopen(path, "w")) == NULL )
    {
        kill(req_pid, SIGTERM);
        return -1;
    }
    if ( (p = find_task_by_vpid(pid)) == NULL )
        fprintf(fp, "No such pid: %d\n", pid);
    else
        fprintf(fp, "name: %s pid: %d\n", p->name, p->pid);
    fclose(fp);
    return 0;
}

static int show_list(int req_pid, const char *path, int debug)
{
	struct lb_config *lbc;
    struct user_config *kuc;
	struct task_relate *tr;
    FILE *fp;
    int i = 0;
	int index;
    char *p;
    if ( (fp = fopen(path, "w")) == NULL )
    {
        kill(SIGTERM, req_pid);
        return -1;
    }
    if (debug)
    {
	show_rbtree(fp);
    fprintf(fp, "TASK_RELATE:\n");
	list_for_each_entry_rcu(lbc, &task_relate, list) {
		fprintf(fp, "%s count:%d \n", lbc->name, lbc->refcount);
		switch(lbc->type)
		{
				case EXEC_RELATE: 
				list_for_each_entry_rcu(tr, &lbc->head, exec_relate) {
							fprintf(fp, " %d", tr->p->pid);
						 }
						 fprintf(fp, "\n");
						 break;
				case SHM_RELATE: 
				list_for_each_entry_rcu(tr, &lbc->head, shm_relate) {
							fprintf(fp, " %d", tr->p->pid);
						 }
						 fprintf(fp, "\n");
						 break;
				case PIPE_RELATE:
				list_for_each_entry_rcu(tr, &lbc->head, pipe_relate) {
							fprintf(fp, " %d",tr->p->pid);
						 }
						 fprintf(fp, "\n");
						 break;
				case NONE_RELATE: 
				default:
								  break;
		}
	}
  } //debug
	list_for_each_entry_rcu(kuc, &user_relate, list) {
        fprintf(fp, "USER_RELATE:#%d\n", ++i);
        fprintf(fp, "\tname:  %s \
                \n\tnumaid:%d  \
                \n\tcounts:%d\n",
                kuc->name, kuc->numaid, kuc->refcount);
		for(index=0, p=kuc->path[index]; p!=NULL; p=kuc->path[++index])
		{
			fprintf(fp, "\trelate path#%d: %s\n", index, p);
		}
        fprintf(fp, "\tpid\tcpu\tpath\n");
        list_for_each_entry_rcu(tr, &kuc->head, user_relate){
            fprintf(fp, "\t%d\t%d\t%s\n", tr->p->pid, latestcpu(tr->p->pid), tr->p->name);
        }
        fprintf(fp, "\n");
    }

    fclose(fp);
	return 0;
}
static struct user_config * get_user_config(void)
{
    struct user_config *ucp;
    ucp = malloc(sizeof(*(ucp)));
    memset(ucp, 0, sizeof(*ucp));
    ucp->numaid = -1;
    INIT_LIST_HEAD(&ucp->list);
    INIT_LIST_HEAD(&ucp->head);
    ucp->refcount = 0;
    ucp->uid = -1;
	ucp->latestcpu = -1;
    return ucp;
}

/* set pid and name to user_relate,
   if pid is not exist, return ESRCH;
   if name is not on task_list, malloc a user_config and 
   attach to user_relate, ucp:{ pid=PID, name=NAME }
actually, should change pid to name with uanr method
pid-> UCP
      ^
 name-|*/
static int user_add_name_pid(int pid, char *newname)
{
    struct user_config *ucp;
    struct task_struct *p;
    struct task_relate *tr;
    struct lb_config *lbc;

    if ( (p = find_task_by_vpid(pid)) == NULL)
    {
        errno = ESRCH;
        return -1;
    }
    if (newname == NULL)
    {
        errno = EINVAL;
        return -1;
    }
    if ((tr = p->tr) == NULL)
        tr = init_task_relate(p);
    if ( tr->user_lbc != NULL)
    {
        if (!strncmp(tr->user_lbc->name, newname, RELEN))
            return 0;   /* already in this list */
        list_del_init(&tr->user_relate);
        --tr->user_lbc->refcount;
        tr->user_lbc = NULL;
		sendmsg_to_kernel(NULL, tr->p->pid);
    }
	/* exist */
    list_for_each_entry_rcu(ucp, &user_relate, list) {
//        if (ucp->uid != task->uid)
//           continue;
        if (is_userconfig(ucp, newname))
        {
            list_add_rcu(&tr->user_relate, &ucp->head);
            tr->user_lbc = ucp;
            ucp->refcount++;
			sendmsg_to_kernel(ucp, tr->p->pid);
            RDprint("attach userconfig:%s count:%d\n",ucp->name, ucp->refcount);
            return 0;
        }
    }
	errno = ENOENT;
	return -1;
/*
    // not find name at user_relate 's list , add it and attach pid to it //
    ucp = get_user_config();
    strcpy(ucp->name, newname);
    //TODO: add UID permitionS
    list_add_rcu(&ucp->list, &user_relate);
//     ucp->uid = task->uid;
    list_add_rcu(&tr->user_relate, &ucp->head);
    tr->user_lbc = ucp;
    ucp->refcount = 1;
	sendmsg_to_kernel(ucp, tr->p->pid);
    RDprint("add userconfig:%s count:%d\n",ucp->name, ucp->refcount);

    // search task_relate to find out "newname", attach the taskS to ucp
    list_for_each_entry_rcu(lbc, &task_relate, list) {
        //TODO: UID permition
        if (!strcmp(lbc->name, newname))    // match
        {
            switch(lbc->type)
            {
#define ADD_EXEC        if ( tr->user_lbc != NULL) \
                        {\
                            list_del_init(&tr->user_relate);\
                            --tr->user_lbc->refcount;\
                            tr->user_lbc = NULL;\
                        }\
                        list_add_rcu(&tr->user_relate, &ucp->head);\
                        tr->user_lbc = ucp;\
                        ucp->refcount ++;
                    case EXEC_RELATE: 
                    list_for_each_entry_rcu(tr, &lbc->head, exec_relate) {
                        ADD_EXEC;
						sendmsg_to_kernel(ucp, tr->p->pid);
                        }
                        break;
                    default:
                        break;
            }//switch
        }// if
    }//for_each
    return 0;
*/
}

/* 
   if exist newname: set the uc's numaid to nid, and issued to kernel;
   if NOT exist newname: ADD newname with {newname, nid}
*/
static int user_set_name_numa(const char *newname, int nid)
{
    struct user_config *ucp;
    struct lb_config *lbc;
    struct task_relate *tr;
    //struct task_struct *p;

    if (newname == NULL)
    {
        errno = EINVAL;
        return -1;
    }
    /* exist */
    list_for_each_entry_rcu(ucp, &user_relate, list) {
//        if (ucp->uid != task->uid)
//           continue;
        if (!strncmp(ucp->name, newname, RELEN))
        {
            ucp->numaid = nid;
            list_for_each_entry_rcu(tr, &ucp->head, user_relate) {
                sendmsg_to_kernel(ucp, tr->p->pid);
            }
            return 0;
        }
    }
    /* NOT exist */
    ucp = get_user_config();
    strcpy(ucp->name, newname);
    ucp->numaid = nid;
    ucp->refcount = 0;
    list_add_rcu(&ucp->list, &user_relate);

    //find task from task_relate's EXEC relate, and  attach to ucp 
    list_for_each_entry_rcu(lbc, &task_relate, list) {
        if (lbc->type != EXEC_RELATE)
                continue;
 //       if (lbc->uid != task->uid)
 //TODO          continue;
        if (is_userconfig(ucp, lbc->name))
        {
            list_for_each_entry(tr, &lbc->head, exec_relate){
                // del original user_relate 
                if (tr->user_lbc != NULL)
                {
                    --tr->user_lbc->refcount;
                    tr->user_lbc = NULL;
                    list_del_init(&tr->user_relate);
                }

                list_add_rcu(&tr->user_relate, &ucp->head);
                tr->user_lbc = ucp;
                ucp->refcount++;
                sendmsg_to_kernel(ucp, tr->p->pid);
                RDprint("attach userconfig:%s count:%d\n",ucp->name, ucp->refcount);
            }
        }
    }
    return -1;
}

/*
   if name exist: ADD rname to path and return;
   if name NOT exist: alloc uc with { name, rname, numaid }
*/
static int user_add_name_rname(const char *name, char *rname, int numaid)
{
        struct user_config *ucp;
        struct lb_config *lbc;
        struct task_relate *tr;
        char *p;
        int i;
        /* exist */
        list_for_each_entry_rcu(ucp, &user_relate, list) {
        //    if (ucp->uid != task->uid)
        //       continue;
            if (!strncmp(name, ucp->name, RELEN))
            {
		        for(i=0, p=ucp->path[i]; p!=NULL; p=ucp->path[++i])
                    if (!strncmp(rname, p, RELEN))
                        return EEXIST;
                /* not in path[...], add to it */
                ucp->path[i] = strdup(rname);
				/* search task_list to "rname", attach it to ucp */
				goto attach;
            }
        }
        /* not exist */
        ucp = get_user_config();
        strcpy(ucp->name, name);
        ucp->path[0] = strdup(rname);
		ucp->numaid = numaid;
        list_add_rcu(&ucp->list, &user_relate);
        ucp->refcount = 0;
        //ucp->uid = p->uid;
attach:
        //find task from task_relate's EXEC relate, and  attach to ucp 
        list_for_each_entry_rcu(lbc, &task_relate, list) {
            if (lbc->type != EXEC_RELATE)
                    continue;
     //       if (lbc->uid != task->uid)
     //TODO          continue;
            if (is_userconfig(ucp, lbc->name))
            {
                list_for_each_entry(tr, &lbc->head, exec_relate){
                    // del original user_relate 
                    if (tr->user_lbc != NULL)
                    {
                        --tr->user_lbc->refcount;
                        tr->user_lbc = NULL;
                        list_del_init(&tr->user_relate);
                    }

                    list_add_rcu(&tr->user_relate, &ucp->head);
                    tr->user_lbc = ucp;
                    ucp->refcount++;
					sendmsg_to_kernel(ucp, tr->p->pid);
                    RDprint("attach userconfig:%s count:%d\n",ucp->name, ucp->refcount);
                }
            }
        }
        return 0;
}

static int user_del_pid(int pid)
{
    struct task_struct *p;
    p = find_task_by_vpid(pid);
    if (p == NULL)
    {  
        errno = ESRCH;
        return -1;
    }
    if (p->tr!=NULL && p->tr->user_lbc!=NULL)
    {
        list_del_init(&p->tr->user_relate);
        p->tr->user_lbc->refcount--;
        p->tr->user_lbc = NULL;
		sendmsg_to_kernel(NULL, p->pid);
    }
    return 0;
}

static int user_del_name(const char *delname)
{
    struct user_config *uc;
    struct task_relate *tr;
    char *p;
    int i;
    int found = 0;
    if ( delname == NULL)
    {
        errno = EINVAL;
        return -1;
    }
    list_for_each_entry_rcu(uc, &user_relate, list){
        if (!strncmp(uc->name, delname, RELEN)) 
        {
            /* this restart and goto statement is used to restart searching from
               head, because _del_ a node while searching mess up the point */
restart:
            list_for_each_entry_rcu(tr, &uc->head, user_relate){
                list_del_init(&tr->user_relate);
                tr->user_lbc->refcount--;
                tr->user_lbc = NULL;
				sendmsg_to_kernel(NULL, tr->p->pid);
                goto restart;
            }
            found = 1;
            break;
        }
    }
    if (found)
    {
        list_del_init(&uc->list);
        /* free path[...] and free uc */
        for(i=0, p=uc->path[i]; p!=NULL; p=uc->path[++i])
            free(uc->path[i]);
        free(uc);
        return 0;
    }
    errno = ENOENT;
    return -1;
}

static void del_userconfig(void)
{
    struct user_config *uc;
    struct task_relate *tr;
    char *p;
    int i;
restart_out:
    list_for_each_entry_rcu(uc, &user_relate, list){
restart_in:
        list_for_each_entry_rcu(tr, &uc->head, user_relate){
            list_del_init(&tr->user_relate);
            tr->user_lbc->refcount--;
            tr->user_lbc = NULL;
            sendmsg_to_kernel(NULL, tr->p->pid);
            goto restart_in;
        }
        list_del_init(&uc->list);
        /* free path[...] and free uc */
        for(i=0, p=uc->path[i]; p!=NULL; p=uc->path[++i])
            free(uc->path[i]);
        free(uc);
        goto restart_out;
    }
}

/* user normal socket to send feed back */
static int send_feed_back(int success, int err)
{
	int ret;
	socklen_t len;
	int i = 10;
#define PORT 6543
	int sock_fd;
	struct sockaddr_in my_addr;
	struct reterr {
		int success;
		int err;
	};
	char buf[MAX_MSGSIZE];

	if ( (sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		syslog(LOG_USER|LOG_INFO, "%s: socket initial failure\n", procname);
		exit(EBUSY);
	}
	my_addr.sin_family = AF_INET;
	my_addr.sin_port   = htons(PORT);
	my_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	len = sizeof(struct sockaddr);
	while(--i)
	{
		ret = connect(sock_fd, (struct sockaddr*)&my_addr, len);
		if (ret < 0 &&  i > 0)
			continue;
		else
			break;
	}
	if (ret < 0)
	{
		syslog(LOG_USER|LOG_INFO, "%s: socket connect failure\n", procname);
		close(sock_fd);
		return 0;
	}
	send(sock_fd, "send buf hello", 14, 0);
	close(sock_fd);
	return 0;
}

/*
   data process
 */
int relate_main(int port)
{
	int sd,ret;
	int group = CN_IDX_RELATE;
	struct sockaddr_nl daddr;
	struct nlmsghdr *nlhdr = NULL;
	struct msghdr msg;
	struct iovec iov;
	struct cn_msg *cnmsg;
	struct user_message *um;

    /* init list head */
    init_lists();

    sd = socket(PF_NETLINK, SOCK_RAW, NETLINK_CONNECTOR);
	if (sd < 0)
	{
	    syslog(LOG_USER|LOG_INFO, "%s: socket open error\n", procname);
		goto err_closed;
	}
    nlhdr = (struct nlmsghdr *) malloc(NLMSG_SPACE(MAX_MSGSIZE));
    if (nlhdr == NULL)
		goto out_free;
    daddr.nl_family = AF_NETLINK;
    daddr.nl_pid = 0;
    daddr.nl_groups = CN_IDX_RELATE;
    if (bind(sd, (struct sockaddr *) &daddr, sizeof(daddr)) == -1)
		goto err_closed;
	ret = setsockopt(sd, SOL_NETLINK,1, &group, sizeof(group));
	if (ret < 0)
	{	
	  	syslog(LOG_USER|LOG_INFO, "%s: setsocket error\n", procname);
		goto out_free;
	}
	/* recv message and process */
    while (1) {
		memset(nlhdr, 0, NLMSG_SPACE(MAX_MSGSIZE));
		memset(&iov, 0, sizeof(struct iovec));
		memset(&msg, 0, sizeof(struct msghdr));
		iov.iov_base = (void *) nlhdr;
		iov.iov_len = NLMSG_SPACE(MAX_MSGSIZE);
		msg.msg_name = (void *) &daddr;
		msg.msg_namelen = sizeof(daddr);
		msg.msg_iov = &iov;
		msg.msg_iovlen = 1;
		ret = recvmsg(sd, &msg, 0);
		if (ret == 0) {
            /* ordinary shutdown,quit */
			_exit(0);
		} else if (ret == -1) {
            /* interrupted by signal.... */
			continue;
		} else {
			cnmsg = (struct cn_msg *) NLMSG_DATA(nlhdr);
			um = (struct user_message *) cnmsg->data;
            if ( um->magic != RELATE_MAGIC )
            {
                RDprint("recived magic not match! %d what: %d\n", um->magic, um->what);
                continue;
            }
			ret = 0;
            switch (um->what)
            {
                case KERN_FORK:
                    RDprint("fork: ppid:%d cpid:%d\n", um->u.fork.ppid, um->u.fork.cpid);
                    record_fork(um->u.fork.ppid, um->u.fork.cpid);
                    break;

                case KERN_EXEC:
                    RDprint("exec: pid:%d name:%s\n", um->u.exec.pid, um->u.exec.path);
                    record_exec(um->u.exec.pid, um->u.exec.path);
                    break;

                case KERN_EXIT:
                    exit_relate(um->u.exit.pid);
                    break;

		        /* name pid */
                case USER_ADD_NAME_P:
                    ret = user_add_name_pid(um->u.uanp.pid, um->u.uanp.name);
                    //TODO: send finish message
                    break;

                /* name numaid */
                case    USER_SET_NAME_N :
                    ret = user_set_name_numa(um->u.uann.name, um->u.uann.numaid);
                    break;

                /* name rname */
                case    USER_ADD_NAME_R :
                    ret = user_add_name_rname(um->u.uanr.name, um->u.uanr.rname, um->u.uanr.numaid);
                    break;

                /* set pid to numa */
                case    UAER_ADD_PID_N:   
                    break;

                /* del PID */
                case     USER_DEL_PID:     
                    ret = user_del_pid(um->u.udpn.pid);
                    break;

                /* DEL NAME */
                case    USER_DEL_NAME: 
                    ret = user_del_name(um->u.udpn.path);
                    break;

                /* SHOW LIST(PID) */
                case USER_SHOW_LIST_ALL:
                    ret = show_list(um->u.show.request_pid, um->u.show.path, um->debug);
                    break;
                case USER_SHOW_LIST_PID:
                    ret = show_list_pid(um->u.show.pid, um->u.show.request_pid, um->u.show.path);
                    break;

                /* trun listen on/off */
                case USER_COMMAND_OFF:
                    change_cn_proc_mode(PROC_CN_MCAST_IGNORE);
                    break;
                case USER_COMMAND_ON:
                    validate_all_pids(NULL);
                    change_cn_proc_mode(PROC_CN_MCAST_LISTEN);
                    break;
                case USER_RESET:
                    del_userconfig();
                    break;
                default:
                    syslog(LOG_USER|LOG_INFO, "%s: what:%d\n", procname, um->what);
                    break;
            }
			send_feed_back(ret, errno);
		}
    }
out_free:
	free(nlhdr);
err_closed:
	close(sd);
	_exit(0);
	return 0;
}
